package me.chan.netty.rpc.client;


import io.netty.channel.Channel;
import io.netty.util.concurrent.DefaultPromise;
import lombok.extern.slf4j.Slf4j;
import me.chan.netty.rpc.client.handler.RpcResponseMessageHandler;
import me.chan.netty.rpc.client.util.SerialNoGenerator;
import me.chan.netty.rpc.msg.RpcRequestMessage;
import me.chan.netty.rpc.server.service.HelloService;

import java.lang.reflect.Proxy;

@Slf4j
public class RpcClient {


    public static void main(String[] args) {
        HelloService service = getProxyService(HelloService.class);
        service.sayHello("2021 China win the all");
        service.sayHello("2020 Tokyo Olympic Games");
    }

    private static  <T> T getProxyService(Class<T> clazz) {

        Class<?>[] interfaces = new Class[]{clazz};

        Object obj = Proxy.newProxyInstance(clazz.getClassLoader(), interfaces, (proxy, method, args) -> {

            RpcRequestMessage msg = new RpcRequestMessage();
            msg.setSequenceId(SerialNoGenerator.generateSerialNo());
            msg.setReturnType(method.getReturnType());
            msg.setMethodName(method.getName());
            msg.setParameterTypes(method.getParameterTypes());
            msg.setInterfaceName(clazz.getName());
            msg.setParameterValues(args);

            Channel channel = RPCClientContext.getChannel();
            channel.writeAndFlush(msg);

            // prepare a promise object to receive result from server
            // channel.eventLoop() method will specify the thread to receive result in async way
            DefaultPromise<?> promise = new DefaultPromise<>(channel.eventLoop());
            RpcResponseMessageHandler.PROMISE_MAP.put(msg.getSequenceId(), promise);

            // wait for the result from server and it will block util the result return
            promise.await();
            if (promise.isSuccess()) {
                return promise.getNow();
            } else {
                throw new RuntimeException(promise.cause());
            }

        });

        return (T) obj;
    }
}
